using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Net.Mail;
using System.Diagnostics;
using System.Security.Principal;

namespace SmartLauncher
{
	/// <summary>
	/// This class is responsible for sending bug report when unhandled errors occur.
	/// It generate an email with:
	/// - The usage context
	/// - The configuration settings
	/// - The error message
	/// - a print screen
	/// - Optionnally, the last log file
	/// </summary>
	public class BugReportManager
	{
		private DateTime _datetime;
		private Exception _exception;
		private string _machine;
		private string _machine2;
		System.Net.IPAddress[] _ip;
		private string _user;
		WindowsIdentity _wi;
		private string _version;
		private string _logfile;
		private string _picfile;
		private string _apppath;
		private System.Configuration.SettingsPropertyValueCollection _settings;

		[DllImport("Gdi32.dll")]
		public static extern int BitBlt(int hDestDC, int x, int y, int nWidth, int nHeight, int hSrcDC, int xSrc, int ySrc, int dwRop);

		[DllImport("User32.dll")]
		public static extern int GetDC(int hwnd);

		[DllImport("User32.dll")]
		public static extern int ReleaseDC(int hwnd, int hdc);


		#region "Constructors and Destructors"
		public BugReportManager()
		{
			AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
			Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
		}
		#endregion

		#region "Exception Handlers"
		void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
		{
			SendBugReport(sender, e.Exception);
		}

		void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
		{
			SendBugReport(sender, (Exception)e.ExceptionObject);
		}

		private void SendBugReport(object sender, Exception e)
		{
			BugReportDialog dlg;

			// === Save the exception into the log file ===============================
			if (Properties.Settings.Default.LogEnabled == true)
				Trace.WriteLine(e);

			// === Collect Bug Report Information =====================================
			GetData();
			_exception = e;

			foreach (Form item in Application.OpenForms)
				item.Hide();

			dlg = new BugReportDialog(this);
			dlg.ShowDialog();

			if (Properties.Settings.Default.SendBugReport == true)
				SendMail();

			System.Diagnostics.Process.GetCurrentProcess().Kill();
			Application.Exit();
		}

		void SendMail()
		{
			SmtpClient svr;
			MailMessage msg;

			try
			{
				svr = new SmtpClient(Properties.Settings.Default.SmtpHost, Properties.Settings.Default.SmtpPort);
				msg = new MailMessage(
					Properties.Settings.Default.SmtpFrom,
					Properties.Settings.Default.SmtpTo,
					string.Format("Bug Report {0} - {1} ver. {2} - {3}@{4}",
						_datetime.ToString("dd/MM/yyyy HH:mm:ss"),
						Application.ProductName,
						_version,
						_user,
						_machine),
					Report);

				if (_logfile != null)
				{
					System.Diagnostics.Trace.Listeners[1].Flush();
					System.Diagnostics.Trace.Listeners[1].Close();
					msg.Attachments.Add(new Attachment(_logfile));
				}
				msg.Attachments.Add(new Attachment(_picfile));

				svr.Send(msg);
			}
			catch (Exception ex)
			{
				MessageBox.Show(string.Format("The Report could not be sent. Please contact your helpdesk.\r\n\r\n{0}", ex.Message), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		private void GetData()
		{
			_datetime = DateTime.Now;
			_machine = Environment.MachineName;
			_machine2 = System.Net.Dns.GetHostName();
			_ip = System.Net.Dns.GetHostEntry(_machine2).AddressList;
			_wi = WindowsIdentity.GetCurrent();
			_user = _wi.Name;
			_version = Application.ProductVersion;
			_apppath = Environment.GetCommandLineArgs()[0];
			_logfile = (Properties.Settings.Default.LogEnabled == true ? string.Format("{0}.log", Path.GetFileNameWithoutExtension(_apppath)) : null);
			_settings = Properties.Settings.Default.PropertyValues;
			_picfile = BuildPrintScreen(string.Format("BugReport_" + Path.GetFileNameWithoutExtension(_apppath)));
		}
		#endregion

		#region "Print Screen Methods"
		private string BuildPrintScreen(string filename)
		{
			Rectangle objRectangle = Screen.PrimaryScreen.Bounds;
			Bitmap objBitmap = new Bitmap(objRectangle.Right, objRectangle.Bottom);
			Graphics objGraphics;
			IntPtr hdcDest;
			int hdcSrc;
			int SRCCOPY = 0xCC0020;
			string strFormatExtension;
			ImageFormat imageFormat = ImageFormat.Png;
			objGraphics = Graphics.FromImage(objBitmap);

			//-- get a device context to the windows desktop and our destination  bitmaps
			hdcSrc = GetDC(0);
			hdcDest = objGraphics.GetHdc();
			//'-- copy what is on the desktop to the bitmap
			BitBlt(hdcDest.ToInt32(), 0, 0, objRectangle.Right, objRectangle.Bottom, hdcSrc, 0, 0, SRCCOPY);
			//-- release device contexts
			objGraphics.ReleaseHdc(hdcDest);
			ReleaseDC(0, hdcSrc);

			strFormatExtension = imageFormat.ToString().ToLower();
			if (Path.GetExtension(filename) != "." + strFormatExtension)
				filename += "." + strFormatExtension;

			if (File.Exists(filename) == true)
				File.Delete(filename);

			if (strFormatExtension == "jpeg")
				BitmapToJPEG(objBitmap, filename, 80);
			else
				objBitmap.Save(filename, imageFormat);

			return filename;
		}

		private void BitmapToJPEG(Bitmap objBitmap, string strFilename)
		{
			BitmapToJPEG(objBitmap, strFilename, 75);
		}

		private void BitmapToJPEG(Bitmap objBitmap, string strFilename, long lngCompression)
		{
			EncoderParameters objEncoderParameters = new EncoderParameters(1);
			ImageCodecInfo objImageCodecInfo = GetEncoderInfo("image/jpeg");

			objEncoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, lngCompression);
			objBitmap.Save(strFilename, objImageCodecInfo, objEncoderParameters);
		}

		ImageCodecInfo GetEncoderInfo(string strMimeType)
		{
			int j;
			ImageCodecInfo[] objImageCodecInfo;

			objImageCodecInfo = ImageCodecInfo.GetImageEncoders();

			j = 0;
			for (j = 0; j < objImageCodecInfo.Length; j++)
				if (objImageCodecInfo[j].MimeType == strMimeType)
					return objImageCodecInfo[j];

			return null;
		}
		#endregion

		#region "Properties"
		public Exception Exception
		{
			get { return _exception; }
		}

		public string Report
		{
			get
			{
				string rc;
				string ip = null;
				int first;
				string config = null;
				string group = null;

				GetData();

				first = 0;
				foreach (System.Net.IPAddress item in _ip)
					ip += string.Format((first++ == 0 ? "\r\nIP addresses    : {0}" : "\r\n                  {0}"), item.ToString());

				first = 0;
				try
				{
					foreach (IdentityReference item in _wi.Groups)
						group += string.Format((first++ == 0 ? "\r\nGroups          : {0}" : "\r\n                  {0}"), item.Translate(typeof(NTAccount)));
				}
				catch (Exception ex)
				{
					group = string.Format("\r\nGroups          : n/c (error occured {0})", ex);
				}

				foreach (System.Configuration.SettingsPropertyValue item in _settings)
					config += string.Format("\r\n   {0} = {1}", item.Name, item.PropertyValue);

				rc = string.Format(
					"Detailed error information follows:" +
					"\r\n" +
					"\r\n===============================" +
					"\r\n=== Environment Information ===" +
					"\r\n===============================" +
					"\r\nDate and Time   : {0}" +
					"\r\nProgram Version : {1}" +
					"\r\nProgram Path    : {2}" +
					"\r\nStart Mode      : {3}" +
					"\r\nLocal repository: {4}" +
					"\r\nTarget appl.    : {5}" +
					"\r\nLog file        : {6}" +
					"\r\nScreen Capture  : {7}" +
					"\r\nMachine Name    : {8}" +
					"{9}" + // Alternate machine name
					"{10}" + // IP addresses
					"\r\nCurrent User    : {11}" +
					"\r\nAuth. Type      : {12}" +
					"\r\nImperson. Level : {13}" +
					"\r\nIs Anonymous    : {14}" +
					"\r\nIs Authenticated: {15}" +
					"\r\nIs Guest        : {16}" +
					"\r\nIs System       : {17}" +
					"{18}" + // NT Groups
					"\r\n" +
					"\r\n===============================" +
					"\r\n=== Application Information ===" +
					"\r\n===============================" +
					"{19}" + // Application configuration
					"\r\n" +
					"\r\n=============================" +
					"\r\n=== Exception Information ===" +
					"\r\n=============================" +
					"\r\n{20}" // Exception
					, _datetime,
					_version,
					_apppath,
					ApplicationStartInfo.StartMode.ToString(),
					ApplicationStartInfo.LocalRoot,
					ApplicationStartInfo.TargetApplication,
					(_logfile == null ? "no log file available" : _logfile),
					_picfile,
					_machine,
					(_machine != _machine2 ? string.Format("\r\nAlternate       : {0}", _machine2) : null),
					ip,
					_user,
					_wi.AuthenticationType,
					_wi.ImpersonationLevel,
					_wi.IsAnonymous,
					_wi.IsAuthenticated,
					_wi.IsGuest,
					_wi.IsSystem,
					group,
					config,
					_exception);

				return rc;
			}
		}

		#endregion
	}
}
